package com.zuul.filter;

import com.alibaba.druid.util.StringUtils;
import com.netflix.zuul.ZuulFilter;
import com.netflix.zuul.context.RequestContext;
import com.zuul.entity.User;
import com.zuul.service.UserService;
import com.zuul.service.feign.RedisService;
import com.zuul.utils.JWT;
import org.codehaus.jettison.json.JSONObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@Component
@ConfigurationProperties(prefix = "auth")
public class AccessTokenFilter extends ZuulFilter {
    private static Logger logger = LoggerFactory.getLogger(AccessTokenFilter.class);

    public List<String> getOpenUrls() {
        return openUrls;
    }

    public void setOpenUrls(List<String> openUrls) {
        this.openUrls = openUrls;
    }

    private List<String> openUrls;

    @Autowired
    private RedisService redisService;
    @Autowired
    private UserService userService;

    /**
     * 过滤器类型
     * pre 路由前被调用
     * routing 路由时被调用
     * post 在routing和error后被调用
     * error 处理请求发生错误时被调用
     *
     * @return
     */
    @Override
    public String filterType() {
        return "pre";
    }

    /**
     * 通过int值来定义过滤器的执行顺序，数值越小优先级越高
     *
     * @return
     */
    @Override
    public int filterOrder() {
        return 2;
    }

    /**
     * 返回一个boolean值来判断该过滤器是否要执行
     *
     * @return
     */
    @Override
    public boolean shouldFilter() {
        return true;
    }


    @Override
    public Object run() {
        RequestContext ctx = RequestContext.getCurrentContext();
        HttpServletRequest request = ctx.getRequest();
        if ("OPTIONS".equals(request.getMethod())) {
            ctx.setSendZuulResponse(true);// 对该请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);// 设值，让下一个Filter看到上一个Filter的状态
            return null;
        }
        if (isOpenUrl(request.getRequestURI())) {
            ctx.setSendZuulResponse(true);// 对该请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);// 设值，让下一个Filter看到上一个Filter的状态
            return null;
        }
        final String token = request.getHeader("token");
        final String uuid = JWT.unsign(token, String.class);
        logger.info("method {} ,url {}", request.getMethod(), request.getRequestURL().toString());
        if (uuid == null) {
            logger.info("uuid 为空");
            returnErrorResponse(ctx,"请登录");
            return null;
        } else {
            //判断用户当前状态是否为正常状态
            logger.info(uuid);
            final User user = userService.selectByUuid(uuid);
            if(user == null ){
                logger.info("用户不存在,可能为务工帮用户");
            }else{
                if (user.getStatus() == 0){
                    returnErrorResponse(ctx,"用户被删除");
                    return null;
                }else if (user.getIsOpen() == 0){
                    returnErrorResponse(ctx,"用户被禁用");
                    return null;
                }else {
                    logger.info("用户状态正常");
                }
            }

            //判断正常用户的防篡改
            final String source = request.getHeader("source");
            if("app".equals(source)){
                logger.info("app登陆");
                boolean loginResult = authLogin(request, ctx, token, "appToken");
                if(!loginResult){
                    return null;
                }
            }else if("pc".equals(source)){
                logger.info("pc登陆");
                boolean loginResult = authLogin(request,ctx,token,"pcToken");
                if(!loginResult){
                    return null;
                }
            }else{
                logger.info("其他登陆");
            }


            ctx.setSendZuulResponse(true);// 对该请求进行路由
            ctx.setResponseStatusCode(200);
            ctx.set("isSuccess", true);// 设值，让下一个Filter看到上一个Filter的状态
            return null;
        }
    }

    private boolean authLogin(HttpServletRequest request, RequestContext ctx, String token, String sourceTokenName){
        String uuid = JWT.unsign(token, String.class);
        if(uuid != null){

            Map map = new HashMap();
            map.put("key","USER_LOGIN_MESSAGE");
            map.put("uuid",uuid);
            Map message = redisService.getMessage(map);
            if(message == null){
                returnErrorResponse(ctx,"token获取失败");
                return false;
            }
            if(message.get("code") == null){
                returnErrorResponse(ctx,"token获取失败");
                return false;
            }
            if(!String.valueOf(message.get("code")).equals("200")){
                returnErrorResponse(ctx,"token获取失败");
                return false;
            }
            final Map<Object, Object> cacheUserInfo = (Map<Object, Object>) message.get("data");

            logger.info("cacheUserInfo: {}",cacheUserInfo);

            if(StringUtils.isEmpty(String.valueOf(cacheUserInfo.get(sourceTokenName)))){
                logger.info("权限变更");
                returnErrorResponse(ctx,"用户角色变更,请重新登录");
                return false;
            }

            if(StringUtils.equals(String.valueOf(cacheUserInfo.get(sourceTokenName)),token)){
                logger.info("同一登陆token");
            }else{
                logger.info("不同登陆token");
                returnErrorResponse(ctx,"用户在其他设备上登录,请重新登录");
                return false;
            }
        }
        return true;
    }

    private void returnErrorResponse(RequestContext ctx, String message){
        ctx.setSendZuulResponse(false);// 过滤该请求，不对其进行路由

        ctx.set("isSuccess", true);
        try{
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",401);
            jsonObject.put("message","auth error");
            jsonObject.put("data",message);
            ctx.setResponseBody(jsonObject.toString());// 返回错误内容
            HttpServletResponse response = ctx.getResponse();
            response.setCharacterEncoding("UTF-8");
            response.setContentType("application/json; charset=utf-8");
            response.setStatus(200);
            response.addHeader("Access-Control-Allow-Origin", ctx.getRequest().getHeader("Origin"));
            response.addHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
            response.addHeader("Access-Control-Allow-Headers", "cache-control, pragma, source, token, x-requested-with");
            response.addHeader("Access-Control-Max-Age", "3600");
            response.addHeader("Access-Control-Allow-Credentials", "true");
        }catch (Exception e){
            e.printStackTrace();
            logger.error("转换json报错",e);
            ctx.setResponseBody("{\"code\":401,\"message\":\"auth error\",\"data\":\""+message+"\"}");// 返回错误内容
        }
    }

    /*private void returnErrorResponse(RequestContext ctx, String message){
        ctx.setSendZuulResponse(true);// 过滤该请求，不对其进行路由
        ctx.setResponseStatusCode(200);// 返回错误码
        ctx.set("isSuccess", true);

        HttpServletResponse response = ctx.getResponse();
        response.setCharacterEncoding("UTF-8");
        response.setContentType("application/json; charset=utf-8");

        PrintWriter out = null ;
        try{
            JSONObject jsonObject = new JSONObject();
            jsonObject.put("code",401);
            jsonObject.put("message","auth error");
            jsonObject.put("data",message);
            out = response.getWriter();
            out.append(jsonObject.toString());
            out.close();
        }
        catch (IOException e) {
            e.printStackTrace();
            logger.error("IO异常报错",e);
            try {
                response.sendError(500);
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }catch (Exception e){
            e.printStackTrace();
            logger.error("转换json报错",e);
            try {
                response.sendError(500);
            } catch (IOException e1) {
                e1.printStackTrace();
                logger.error("response set 报错",e);
            }
        }
    }*/


    boolean isOpenUrl(String userAccessUrl) {

        for (String url : openUrls
                ) {
            if (userAccessUrl.matches(url)) {
                return true;
            }
        }
        return false;
    }

}

